﻿using System;

namespace Bettzueche.RLGlue.Generics {

    /// <summary>
    /// Das Lernexperiment führt über RL-Glue das Experiment aus und beobachtet es.
    /// <para>
    /// Alle Experimente <b>können</b> die Methoden von AbstractExperiment implementieren und aufrufen.
    /// </para>
    /// </summary>
    /// <typeparam name="O">Datentyp der Zustände; Sollte int, double oder char sein</typeparam>
    /// <typeparam name="A">Datentyp der Aktionen; Sollte int, double oder char sein</typeparam>
    /// <seealso cref="AnEnvironment"/>
    /// <seealso cref="AnAgent"/>
    /// <remarks>
    /// Normalerweise ist das Schreiben des Experimentprogramms der kürzeste und einfachste Teil beim Schreiben Ihres
    /// ersten Lern-Experiments. Das Experiment Programm hat kein Interface zu implementieren und umfasst vor Allem Anrufe
    /// bereits bestehender RL-Glue-Funktionen. Das Experiment hat vier Hauptaufgaben:<para>
    /// a) Starte das Experiment
    /// b) Spezifiziere die Sequenz der Agenten-Umwelt-Interaktionen (Schritte)
    /// c) Extraktion und Analyse experimenteller Daten
    /// d) Versuch beenden und aufräumen.</para><para>
    /// Das Experiment Programm kann nur die RL-Glue-Interface-Funktionen aufrufen. Es kann auf keine Agenten oder Umwelt
    /// Funktionen direkt zugreifen.</para>
    /// </remarks>
    abstract class AbstractGenericExperiment<O,A>
        where O : struct
        where A : struct
    {
        /// <summary>
        /// Anzahl ausgeführter Schritte in der letzten oder aktuellen Episode.
        /// </summary>
        public int RL_num_steps { get; protected set; }

        /// <summary>
        /// Gesamtbelohnung der letzten oder aktuellen Episode.
        /// </summary>
        public int RL_return { get; protected set; }

        /// <summary>
        /// Initialisierung der Umwelt, des Agenten und interner Datenstrukturen.
        /// </summary>
        /// <returns></returns>
        public abstract String RL_init();

        /// <summary>
        /// Startet Umwelt und Agenten.
        /// </summary>
        /// <returns></returns>
        public abstract Observation_Action<O,A> RL_start();

        /// <summary>
        /// Führt einen Lernschritt aus (bspw. Env_step und Agent_step).
        /// </summary>
        /// <returns>Daten, die durch den Lern-Schritt produziert wurden</returns>
        /// <remarks>
        /// Beispielhafte (typische) Anwendung und Implemtierung:<para>
        /// Die RL-step Methode ruft <see cref="AnEnvironment.Env_step">Env_step</see> mit der jüngsten Agentenaktion 
        /// (zu Beginn die von Agent_start) auf.
        /// Es wird die neue Beobachtung (bzw. Folgezustand), die Belohnung und das Terminierungsflag zurückgegeben.
        /// Wenn das Flag nicht gesetzt ist, wird <see cref="AnAgent.Agent_step">Agent_step</see> mit der Belohnung und 
        /// Beobachtung als Argumente aufgerufen. Die Aktion, die Agent_step liefert, wird von RL-Glue bis zum nächsten Aufruf 
        /// von RL_step gespeichert.
        /// Wenn das Flag gesetzt ist, wird <see cref="AnAgent.Agent_end">Agent_end</see> mit dem Reward aufgerufen.
        /// Dieser Prozess wird fortgesetzt, bis entweder das Flag gesetzt oder bspw. 100 Schritte ausgeführt wurden.
        /// </para>
        /// <code>
        /// 1. RL_init()
        /// 2. RL_start()
        /// 3. steps=0
        /// 4. terminal=false 
        /// 5. while steps &lt; 100 and not terminal
        /// 6.     terminal,reward,observation,action = RL_step()
        /// 7.     steps=steps+1
        /// 8. RL_cleanup()
        /// </code>
        /// </remarks>
        public abstract Reward_Observation_Action_Terminal<O,A> RL_step();

        /// <summary>
        /// Aufräumarbeiten nach Ende eines Experiments.
        /// </summary>
        public abstract void RL_cleanup();

        /// <summary>
        /// Sendet Nachrichten (Befehle, Parameter o.ä.) an den Agenten
        /// </summary>
        /// <param name="message">Nachricht an den Agenten</param>
        /// <returns>Antwort des Agenten</returns>
        public abstract String RL_agent_message(String message);

        /// <summary>
        /// Sendet Nachrichten (Befehle, Parameter o.ä.) an die Umwelt.
        /// </summary>
        /// <param name="message">Nachricht an die Umwelt</param>
        /// <returns>Antwort der Umwelt</returns>
        public abstract String RL_env_message(String message);


        /// <summary>
        /// Führt die angegebene Anzahl <see cref="RL_step">RL_steps</see> aus.
        /// </summary>
        /// <param name="num_steps">Anzahl (maximal) auszuführender Schritte. Bspw. auch 0, um ohne Schrittlimit bis zu einem
        /// Endzustand zu gelangen.</param>
        /// <returns>??</returns>
        public abstract int RL_episode(uint num_steps);

    }
}
